#ifndef _VOXELIZE_H_
#define _VOXELIZE_H_

#include "HierarchicalTree.h"
#include "RegularGrid.h"
#include "RegularGrid2.h"
#include "Intersect.h"

#include "..\xobjIO\glMesh.h"
#include "..\xobjIO\XobjIO.h"

static bool Voxelize(SDistanceField_host & grid, const C_Actor & xobj)
{
    float3 f3whd = xobj.m_AABB.pmax - xobj.m_AABB.pmin;
    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
    float3 f3whdSmall = f3whd * invGridDim;

    float fwhd = length(f3whd);
    float fwhdSmall = length(f3whdSmall);
    float tmin = fwhd;

    // go from pmin to pmax
    // shift into the center of first voxel cube
    float3 pmin = xobj.m_AABB.pmin + f3whdSmall * 0.5f; 

    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
    {
        pmin.y = xobj.m_AABB.pmin.y + f3whdSmall.y * 0.5f;
        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
        {
            pmin.z = xobj.m_AABB.pmin.z + f3whdSmall.z * 0.5f;
            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
            {
                tmin = fwhd;
                float3 tbg = {fwhd, 0, 0};

                for (uint32 iFace = 0; iFace < xobj.posIdx.size(); iFace+=3)
                {
                    int3 idx = { xobj.posIdx[iFace + 0], 
                                 xobj.posIdx[iFace + 1], 
                                 xobj.posIdx[iFace + 2] };

                    if (idx.x == idx.y || idx.x == idx.z || idx.y == idx.z)
                        continue;

                    float3 v0 = xyz(xobj.pos[idx.x]);
                    float3 v1 = xyz(xobj.pos[idx.y]);
                    float3 v2 = xyz(xobj.pos[idx.z]);

                    STriangle triangle;

                    Triangle(&triangle, v0, v1, v2);

                    bool bIntersect = Intersect(triangle, pmin, tbg);
                    if (bIntersect)
                    {
                        if (abs(tbg.x) < fwhdSmall && abs(tmin) > abs(tbg.x))
                            tmin = tbg.x;
                    }
                }
                Set(&grid, tmin, ix, iy, iz);

                pmin.z += f3whdSmall.z;
            }

            pmin.y += f3whdSmall.y;
        }

        pmin.x += f3whdSmall.x;
    }

    return true;
}

static bool Voxelize(SDistanceField_host & grid, C_Actor * pXobj, uint32 nXobj)
{
    S_Box AABB = pXobj[0].m_AABB;
    for (uint32 ixobj = 1; ixobj < nXobj ;ixobj++)
    {
        AABB.pmin = min( AABB.pmin, pXobj[ixobj].m_AABB.pmin );
        AABB.pmax = max( AABB.pmax, pXobj[ixobj].m_AABB.pmax );
    }

    float3 f3whd = AABB.pmax - AABB.pmin;
    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
    float3 f3whdSmall = f3whd * invGridDim;

    float fwhd = length(f3whd);
    float fwhdSmall = length(f3whdSmall);
    float tmin = fwhd;

    // go from pmin to pmax
    // shift into the center of first voxel cube
    float3 pmin = AABB.pmin + f3whdSmall * 0.5f; 

    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
    {
        pmin.y = AABB.pmin.y + f3whdSmall.y * 0.5f;
        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
        {
            pmin.z = AABB.pmin.z + f3whdSmall.z * 0.5f;
            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
            {
                tmin = fwhd;
                float3 tbg = {fwhd, 0, 0};

                for (uint32 ixobj = 0; ixobj < nXobj; ixobj++)
                {
                    for (uint32 iFace = 0; iFace < pXobj[ixobj].posIdx.size(); iFace += 3)
                    {
                        int3 idx = { pXobj[ixobj].posIdx[iFace + 0], 
                                     pXobj[ixobj].posIdx[iFace + 1], 
                                     pXobj[ixobj].posIdx[iFace + 2] };

                        float3 v0 = xyz( pXobj[ixobj].pos[idx.x] );
                        float3 v1 = xyz( pXobj[ixobj].pos[idx.y] );
                        float3 v2 = xyz( pXobj[ixobj].pos[idx.z] );

                        SPlane plane;
                        STriangle triangle;

                        Plane(&plane, v0, v1, v2);
                        Triangle(&triangle, v0, v1, v2);

                        bool bIntersect = Intersect(triangle, pmin, tbg);
                        if (bIntersect)
                        {
                            if (abs(tbg.x) < fwhdSmall && abs(tmin) > abs(tbg.x))
                                tmin = tbg.x;
                        }
                    }
                }
                Set(&grid, tmin, ix, iy, iz);

                pmin.z += f3whdSmall.z;
            }
            pmin.y += f3whdSmall.y;
        }
        pmin.x += f3whdSmall.x;
    }

    return true;
}

static float Voxelize(int4Octree & tree, TNode<8> *pNode, C_Actor *pXobj, const float3 & point, S_Box aabb, int3 dim)
{
    float tmin = 1e+38;

    if (pNode != NULL)
    {  
        for (int idx = 0; idx < pNode->m_Count; idx++)
        {
            int4 i4 = tree.m_pData[pNode->m_Start + idx];
            float4 v0 = pXobj[i4.w].pos[i4.x];
            float4 v1 = pXobj[i4.w].pos[i4.y];
            float4 v2 = pXobj[i4.w].pos[i4.z];

            SPlane plane;
            STriangle triangle;

            Plane(&plane, xyz(v0), xyz(v1), xyz(v2));
            Triangle(&triangle, v0, v1, v2);

            float3 tbg = {0.0f, 0.0f, 0.0f};

            bool bIntersect = Intersect(triangle, point, tbg);
            if (bIntersect)
            {
                if (abs(tmin) > abs(tbg.x))
                    tmin = tbg.x;
            }
        }

        float3 l3 = aabb.pmax - aabb.pmin;

        float3 invDim = { 1.0f / dim.x, 1.0f / dim.y, 1.0f / dim.z};
        
        int3 nodeId = CompareTo(aabb, point, dim);

        float3 pmin = aabb.pmin;
        pmin.x += nodeId.x * l3.x * invDim.x;
        pmin.y += nodeId.y * l3.y * invDim.y;
        pmin.z += nodeId.z * l3.z * invDim.z;

        float3 pmax = aabb.pmax;
        pmax.x -= (dim.x - 1 - nodeId.x) * l3.x * invDim.x;
        pmax.y -= (dim.y - 1 - nodeId.y) * l3.y * invDim.y;
        pmax.z -= (dim.z - 1 - nodeId.z) * l3.z * invDim.z;

        AABox(&aabb, pmin, pmax);

        int NodeIdx = nodeId.x + (nodeId.y + nodeId.z * dim.y) * dim.x;

        if (NodeIdx >= 0 && NodeIdx < 8)        
            tmin = min(Voxelize(tree, pNode->m_ppNode[NodeIdx], pXobj, point, aabb, dim), tmin);
    }

    return tmin;
}

static bool Voxelize(SDistanceField_host & grid, 
                     int4Octree     & tree, 
                     C_Actor        * pXobj, 
                     uint32           nXobj)
{
    S_Box AABB = pXobj[0].m_AABB;

    for (uint32 ixobj = 1; ixobj < nXobj ;ixobj++)
    {
        AABB.pmin = min( AABB.pmin, pXobj[ixobj].m_AABB.pmin );
        AABB.pmax = max( AABB.pmax, pXobj[ixobj].m_AABB.pmax );
    }

    AABB.pmin -= f3(0.01f, 0.01f, 0.01f);
    AABB.pmax += f3(0.01f, 0.01f, 0.01f);

    float3 f3whd = AABB.pmax - AABB.pmin;
    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
    float3 f3whdSmall = f3whd * invGridDim;

    float fwhd = length(f3whd);
    float fwhdSmall = length(f3whdSmall) * 0.5;
    float tmin = fwhd;

    // go from pmin to pmax
    // shift into the center of first voxel cube
    float3 pmin = AABB.pmin + f3whdSmall * 0.5f; 

    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
    {
        pmin.y = AABB.pmin.y + f3whdSmall.y * 0.5f;
        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
        {
            pmin.z = AABB.pmin.z + f3whdSmall.z * 0.5f;

            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
            {
                float tmin = Voxelize(tree, tree.m_pRoot, pXobj, pmin, AABB, i3(2, 2, 2));
                
                Set(&grid, tmin, ix, iy, iz);

                pmin.z += f3whdSmall.z;
            }
            pmin.y += f3whdSmall.y;
        }
        pmin.x += f3whdSmall.x;
    }

    return true;
}

static bool Voxelize(Sf_Grid & grid, 
                     int4Octree      & tree, 
                     C_Actor         * pXobj, 
                     uint32            nXobj)
{
    S_Box AABB = pXobj[0].m_AABB;

    for (uint32 ixobj = 1; ixobj < nXobj ;ixobj++)
    {
        AABB.pmin = min( AABB.pmin, pXobj[ixobj].m_AABB.pmin );
        AABB.pmax = max( AABB.pmax, pXobj[ixobj].m_AABB.pmax );
    }

    AABB.pmin -= f3(0.01f, 0.01f, 0.01f);
    AABB.pmax += f3(0.01f, 0.01f, 0.01f);

    float3 f3whd = AABB.pmax - AABB.pmin;
    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
    float3 f3whdSmall = f3whd * invGridDim;

    float fwhd = length(f3whd);
    float fwhdSmall = length(f3whdSmall) * 0.5;
    float tmin = fwhd;

    // go from pmin to pmax
    // shift into the center of first voxel cube
    float3 pmin = AABB.pmin + f3whdSmall * 0.5f; 

    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
    {
        pmin.y = AABB.pmin.y + f3whdSmall.y * 0.5f;
        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
        {
            pmin.z = AABB.pmin.z + f3whdSmall.z * 0.5f;

            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
            {
                float tmin = Voxelize(tree, tree.m_pRoot, pXobj, pmin, AABB, i3(2, 2, 2));

                Set(&grid, tmin, ix, iy, iz);

                pmin.z += f3whdSmall.z;
            }
            pmin.y += f3whdSmall.y;
        }
        pmin.x += f3whdSmall.x;
    }

    return true;
}

static bool Voxelize(Sf_Grid & grid, 
                     CglMesh         * pMesh, 
                     uint32            nMesh)
{
}
#endif